package top.zhangjianyong.blog.service.impl;

import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.zhangjianyong.blog.client.OssClient;
import top.zhangjianyong.blog.entity.dto.FileDTO;
import top.zhangjianyong.blog.entity.po.File;
import top.zhangjianyong.blog.enums.FileType;
import top.zhangjianyong.blog.enums.RespCode;
import top.zhangjianyong.blog.mapper.FileMapper;
import top.zhangjianyong.blog.params.file.UploadParam;
import top.zhangjianyong.blog.response.ApiResponse;
import top.zhangjianyong.blog.service.FileService;
import top.zhangjianyong.blog.utils.FileUtils;
import top.zhangjianyong.blog.utils.LocalDateUtil;
import top.zhangjianyong.blog.enums.FileExtension;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件service
 *
 * @author zhangjianyong
 * @since 2024/7/25 下午11:03
 */
@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {


    @Autowired
    private OssClient ossClient;

    private final FileService fileService;

    public FileServiceImpl(@Lazy FileService fileService) {
        this.fileService = fileService;
    }

    @Override
    public ApiResponse upload(UploadParam param) {
        try {
            List<File> file = fileService.save(param.files(), param.type());
            List<FileDTO> list =
                file.stream()
                    .map(
                        f -> {
                            FileDTO fileDTO = new FileDTO();
                            BeanUtils.copyProperties(f, fileDTO);
                            return fileDTO;
                        })
                    .toList();
            return ApiResponse.success(list);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return ApiResponse.fail(RespCode.FILE_UPLOAD_ERROR);
        }
    }

    @Override
    public ApiResponse get(Long id, HttpServletResponse response) {
        File file = getById(id);
        if (file == null) {
            return ApiResponse.fail(RespCode.FILE_NOT_FUND);
        }

        // 从OSS获取文件并写入响应
        var ossObject = ossClient.getObject(file.objectKey());
        try (var inputStream = ossObject.getObjectContent();
             var outputStream = response.getOutputStream()){

            // 根据文件扩展名设置正确的响应头
            String contentType = FileExtension.getContentTypeByExtension(file.getExtension());
            response.setContentType(contentType);
            
            // 根据文件类型决定是否作为附件下载
            if (FileExtension.isImageFile(file.getOriginalName())) {
                // 图片文件直接显示，不作为附件下载
                response.setHeader("Content-Disposition", "inline; filename=\"" + file.getFileName() + "\"");
            } else {
                // 其他文件作为附件下载
                response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getFileName() + "\"");
            }

            // 复制流
            inputStream.transferTo(outputStream);
            return ApiResponse.success();
        } catch (Exception e) {
            log.error("从OSS获取文件失败: {}", e.getMessage(), e);
            return ApiResponse.fail(RespCode.FILE_READ_ERROR);
        }
    }

    @Override
    public File save(MultipartFile file, Integer type) throws IOException {
        String md5 = new MD5().digestHex(file.getInputStream());
        String originalName = file.getOriginalFilename();
        String extension = FileUtils.getExtension(originalName);
        String fileName = md5 + "." + extension;
        // 查询同名文件存在即返回
        File f = lambdaQuery()
            .eq(File::getFileName, fileName)
            .eq(File::getType, type)
            .one();
        if (f != null) {
            return f;
        }

        FileType fileType = FileType.getByType(type);
        String rootPath = fileType.getRootPath();
        String datePath = LocalDateUtil.dateToStr();
        f = File.builder()
            .originalName(originalName)
            .rootPath(rootPath)
            .datePath(datePath)
            .fileName(fileName)
            .bucketName(ossClient.getBucketName())
            .extension(extension)
            .fileSize(file.getSize())
            .md5(md5)
            .status(1)
            .type(type)
            .build();


        // 上传OSS
        ossClient.putObject(f.objectKey(), file.getInputStream());

        // 保存文件记录
        save(f);
        return f;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<File> save(List<MultipartFile> files, Integer type) throws IOException {
        List<File> fileList = new ArrayList<>();
        for (MultipartFile file : files) {
            File save = save(file, type);
            fileList.add(save);
        }

        return fileList;
    }


}
